﻿using System;
using OpenTK;
using AnimatingHair.Auxiliary;

namespace AnimatingHair.Entity
{
    /// <summary>
    /// An early version of a Bust representation - it is simplified into a sphere (head) and two cylinders (neck and shoulders)
    /// </summary>
    class Bust
    {
        // TODO: friction force on particles - Coulomb's model

        private static bool interactsAboveSurface = true;
        private const double interactionDistance = 0.1;

        public Geometry.Sphere Head { get; set; }
        public Geometry.Cylinder Neck { get; set; }
        public Geometry.Cylinder Shoulders { get; set; }
        public Geometry.Sphere ShoulderTipLeft { get; set; }
        public Geometry.Sphere ShoulderTipRight { get; set; }


        public void CalculateForcesOnParticles( SPHParticle[] particles )
        {
            foreach ( SPHParticle particle in particles )
            {
                if ( particle is HairParticle && (particle as HairParticle).IsRoot )
                    continue;

                applySphereCollisionForce( Head, particle );

                applySphereCollisionForce( ShoulderTipLeft, particle );
                applySphereCollisionForce( ShoulderTipRight, particle );

                applyCylinderCollisionForce( Neck, particle );

                applyCylinderCollisionForce( Shoulders, particle );
            }
        }

        private static void applySphereCollisionForce( Geometry.Sphere sphere, SPHParticle particle )
        {
            Vector3d normal = particle.Position - sphere.Center;
            double distance = normal.Length;
            normal = normal / distance;

            if ( interactsAboveSurface )
            // starts ineracting slightly above the surface of sphere:
            {
                if ( distance < sphere.Radius + interactionDistance && distance > sphere.Radius )
                {
                    double magnitude = 1 - ((distance - sphere.Radius) / (interactionDistance));
                    particle.Force += magnitude * normal;
                }
                else if ( distance < sphere.Radius )
                {
                    particle.Force += normal;
                }
            }
            else
            // starts ineracting slightly below the surface of sphere:
            {
                if ( (distance < sphere.Radius) && (distance > sphere.Radius - interactionDistance) )
                {
                    double magnitude = 1 - ((distance - (sphere.Radius - interactionDistance)) / (interactionDistance));
                    particle.Force += magnitude * normal;
                }
                else if ( distance < sphere.Radius - interactionDistance )
                {
                    particle.Force += normal;
                }
            }
        }

        private static void applyCylinderCollisionForce( Geometry.Cylinder cylinder, SPHParticle particle )
        {
            // TODO: optimizovat tieto vypocty
            if ( !Geometry.PointInInfiniteRadiusCylinder( cylinder, particle.Position ) )
                return;

            Vector3d normal = Geometry.LineToPointNormal( cylinder.Endpoint1, cylinder.Endpoint2, particle.Position );
            double distance = normal.Length;
            normal = normal / distance;

            if ( interactsAboveSurface )
            // starts ineracting slightly above the surface of sphere:
            {
                if ( distance < cylinder.Radius )
                {
                    particle.Force += normal;
                }
                else if ( (distance < cylinder.Radius + interactionDistance) && (distance > cylinder.Radius) )
                {
                    double magnitude = 1 - ((distance - cylinder.Radius) / (interactionDistance));
                    particle.Force += magnitude * normal;
                }
            }
            else
            // starts ineracting slightly below the surface of sphere:
            {
                if ( distance < cylinder.Radius - interactionDistance )
                {
                    particle.Force += normal;
                }
                else if ( (distance < cylinder.Radius) && (distance > cylinder.Radius - interactionDistance) )
                {
                    double magnitude = 1 - ((distance - (cylinder.Radius - interactionDistance)) / (interactionDistance));
                    particle.Force += magnitude * normal;
                }
            }
        }
    }
}